


/*--------------------------------------------------------------*/
/*  Copyright (c) 2006 by the University of Oregon.		*/
/*  See the COPYRIGHT file in this directory for permission	*/
/*  to use and distribute this software.			*/
/*--------------------------------------------------------------*/

// $Id: AutoOptionList.cpp,v 1.3 2003/01/13 20:53:06 ndunn Exp $

// AutoOptionList.C -- implementation of AutoOptionList class			

// John Conery								
// University of Oregon							
// May 2000								

#include <sstream>
using std::ostringstream;

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ; 

#include <iostream>
using std::cerr ; 
using std::cout ; 
using std::endl ; 

#include <iterator>
using std::ostream_iterator ; 


#include <map>
using std::map ; 


#include <cstdlib>
#include <algorithm>
#include <limits.h>


#include "AutoOptionList.hpp"

#include "StringTokenizer.hpp"
using gnuworld::StringTokenizer ; 

#include "GeneralType.hpp"
#include "IntType.hpp"
#include "DoubleType.hpp"
#include "FlagType.hpp"
#include "CharType.hpp"
#include "OptionType.hpp"

// Copy the command line arguments to a local list.  Start at
// argv[1] since we don't want the program name (argv[0]).

AutoOptionList::AutoOptionList(int argc, char *argv[]) {

	mNumArgs = argc ; 

	if(mNumArgs> 0){
		setBinary(argv[0]) ; 
	}

	for (int i = 1; i < argc; i++){
		mInputList.push_back(string(argv[i]));
	}

}

string AutoOptionList::getStandardOptionsText() 
{
  string returnString ; 
		returnString +=  "\t-h \t Help, this message\n"   ;
		returnString +=  "\t-xml \t Xml output\n"   ;
		returnString +=  "\t-opts \t Selected options\n"  ;
		returnString +=  "\t-n \t Dry run.\n"  ;
  return returnString ; 
}


// Delete the list and any items left

AutoOptionList::~AutoOptionList() {
  mInputList.clear();
}

// Local procedures to search the list for command names.  One
// version is used when a command expects a value -- it returns
// the string that immediately follows the command, or else
// "" if the command isn't in the list.  For error checking,
// the procedure makes sure the argument exists (i.e. the user
// didn't give a command as the last item on the line) and the
// value doesn't start with a hyphen.  If the command is found,
// it is removed from the list, and if the argument is valid,
// it is also removed.

// The second procedure is used if a command is a simple flag; 
// if the command is in the list the procedure removes the command
// and returns true.
//
// Future addition: return a list of values up to but not
// including the next command.

string removeArg(const string &name, list<string> &L) {
  string argvalue = "";
  string argname = "-" + name;

  list<string>::iterator ptr = find(L.begin(), L.end(), argname);
  list<string>::iterator next = L.end();

  if (ptr != L.end()) {
    next = ptr;
    advance(next,1);
    L.erase(ptr);
		// if not the last
    if (
			((next != L.end()) && 
			// this might be a flag
			((*next)[0] != '-') ) 
				||
			// unless it is a negative number
			(
			  ((*next)[0] == '-') ) &&
				( ((int) (*next)[1]) < '9' ) &&
				((*next)[1] != '.') 
			)
			 {
      argvalue = *next;
      L.erase(next);
    }
  }

  return argvalue;
}



bool removeFlag(const string &name, list<string> &L) {
  string argname = "-" + name;

  list<string>::iterator ptr = find(L.begin(), L.end(), argname);

  if (ptr != L.end()) {
    L.erase(ptr);
    return true;
  }
  else {
    return false;
  }
}



// Public methods -- getInt(), getDouble(), and getOption() call
// removeArg() to locate and remove a command and its argument;
// getFlag() calls removeFlag() to locate and remove a single command.

int AutoOptionList::getInt(const string &name, int idefault, int min, int max) {
  int argvalue = idefault;
  string arg = removeArg(name,mInputList);
  if (arg.length() > 0){
    argvalue = atoi(arg.c_str());
	}
  return argvalue;
}

double AutoOptionList::getDouble(const string &name, double ddefault, double min, double max) {
  double argvalue = ddefault;
  string arg = removeArg(name,mInputList);
  if (arg.length() > 0){
    	argvalue = atof(arg.c_str());
  }
  return argvalue;
}

bool AutoOptionList::getFlag(const string &name, bool bdefault) {
  if (removeFlag(name,mInputList))
    return true;
  else
    return bdefault;
}

char AutoOptionList::getChar(const string &name, const char &sdefault) {
  char argvalue = sdefault;
  string arg = removeArg(name,mInputList);
  if (arg.length() == 1 )
    argvalue = arg[0];
  return argvalue;
}


string AutoOptionList::getOption(const string &name, const string &sdefault) {
  string argvalue = sdefault;
  string arg = removeArg(name,mInputList);
  if (arg.length() > 0)
    argvalue = arg;
  return argvalue;
}

// Debugging utility -- print the contents of the list:

void AutoOptionList::print() const {
  copy(mInputList.begin(), mInputList.end(), ostream_iterator<string>(cout,"\n"));
}

// Return a copy of the list:

list<string> &AutoOptionList::contents() {
  return mInputList;
}

// Return the next string in the list -- don't do this until
// all the options are removed...

string AutoOptionList::next() {
  string s = mInputList.front();
  mInputList.pop_front();
  return s;
}

// See if the list is empty

bool AutoOptionList::empty() const {
  return (mInputList.size() == 0);
}


void AutoOptionList::setBinary(string binaryDescription) 
{
	mBinaryString = binaryDescription ; 
}

string AutoOptionList::getOptionValueText() 
{
	string returnString = "" ; 
  list<GeneralType*>::iterator iter = mVariableList.begin() ; 

	map<string,GeneralType*> groupList  = getGroupList() ; 
	map<string,GeneralType*>::iterator groupIter = groupList.begin() ; 

	while(groupIter != groupList.end()){

		if(groupIter->first!="default"){
			returnString += "\n  " ; 
			returnString += groupIter->first ; 
		}

		iter = mVariableList.begin() ;

		while(  iter!= mVariableList.end()){
			if( (*iter)->getGroup()== groupIter->first ){
//        returnString += "\n" ; 
				returnString += " " ; 
			  returnString += (*iter)->getFlag() ; 
				returnString += "[" ; 
				returnString += (*iter)->getValueAsString() ;
				returnString += "]" ; 
			}
			iter++ ; 
		}

		++groupIter ; 
	}
	return returnString ; 
}


string AutoOptionList::getHelpText() 
{
	string returnString = getUsageText() ; 
  list<GeneralType*>::iterator iter = mVariableList.begin() ; 

	map<string,GeneralType*> groupList  = getGroupList() ; 
	map<string,GeneralType*>::iterator groupIter = groupList.begin() ; 

	while(groupIter != groupList.end()){

		if(groupIter->first!="default"){
			returnString += "\n  " ; 
			returnString += groupIter->first ; 
		}

		iter = mVariableList.begin() ;

		while(  iter!= mVariableList.end()){
			if( (*iter)->getGroup()== groupIter->first ){
				returnString += "\n" ; 
				returnString += (*iter)->getOptionText() ;
			}
			iter++ ; 
		}

		++groupIter ; 
	}

	returnString += "\n" ; 
	returnString += getStandardOptionsText() ; 

//  while(  iter!= mVariableList.end()){
//    returnString += "\n" ; 
//    returnString += (*iter)->getOptionText() ;
//     iter++ ; 
//  }
	return returnString ; 
}

map<string,GeneralType*> AutoOptionList::getGroupList() 
{
	map<string,GeneralType*> returnList ; 
  list<GeneralType*>::iterator iter = mVariableList.begin() ; 
	string groupName ; 
	while(  iter!= mVariableList.end()){
		groupName = (*iter)->getGroup() ; 
		if( returnList.find( groupName)== returnList.end()){
			returnList[groupName] = (*iter) ; 
//      cout << "adding group name: " << groupName << endl ;
		}
    iter++ ; 
	}

	return returnList ; 
}


string AutoOptionList::getUsageText() 
{
	string returnString = "Usage: " ; 
	returnString += mBinaryString ; 
	returnString += " <options>" ; 
	return returnString ; 
}

string AutoOptionList::getOptionXML() 
{
	string returnString = "" ; 

	returnString += "<app>" ; 
	returnString += "\n" ; 
	returnString += "<binary>" ; 
	returnString += "\n" ; 
	returnString += mBinaryString  ; 
	returnString += "\n" ; 
	returnString += "</binary>" ; 
	returnString += "\n" ; 
	returnString += "<options>" ; 
	returnString += "\n" ; 

  list<GeneralType*>::iterator iter = mVariableList.begin() ; 

	map<string,GeneralType*> groupList  = getGroupList() ; 
	map<string,GeneralType*>::iterator groupIter = groupList.begin() ; 

	while(groupIter != groupList.end()){

		returnString += "\n  " ; 
		returnString += "<group name=\"" ; 
		returnString += groupIter->first ; 
		returnString += "\">" ; 

		iter = mVariableList.begin() ;

		while(  iter!= mVariableList.end()){
			if( (*iter)->getGroup()== groupIter->first ){
				returnString += "\n" ; 
				returnString += "<option>" ; 
				returnString += "\n" ; 
				returnString += (*iter)->getOptionXML() ;
				returnString += "\n" ; 
				returnString += "</option>" ; 
			}
			iter++ ; 
		}

		++groupIter ; 
		returnString += "\n" ; 
		returnString += "</group>" ; 
	}


	returnString += "\n" ; 
	returnString += "</options>" ; 
	returnString += "\n" ; 
	returnString += "</app>" ; 

	return returnString ; 
}


void AutoOptionList::addFlag(bool* variable,string flag,string shortDescription,bool defaultValue/*false*/, string longDescription/*""*/,string group/*"default" */, int order/*-1*/) 
{
//  cout << "adding flag with group: " << group << endl ; 
	mVariableList.push_back( new FlagType(variable,flag,shortDescription,defaultValue,longDescription,group,order)) ;  
}

void AutoOptionList::addInt(int* variable,string flag,string shortDescription,int defaultValue/*-1*/, string longDescription/*""*/,string group/*"default" */, int order/*-1*/) 
{
	mVariableList.push_back( new IntType(variable,flag,shortDescription,defaultValue,longDescription,group,order)) ;  
}

void AutoOptionList::addChar(char* variable,string flag,string shortDescription,char defaultValue/*'a'*/, string longDescription/*""*/,string group/*"default" */, int order/*-1*/) 
{
	mVariableList.push_back( new CharType(variable,flag,shortDescription,defaultValue,longDescription,group,order)) ;  
}

void AutoOptionList::addDouble(double* variable,string flag,string shortDescription,double defaultValue/*'a'*/, string longDescription/*""*/,string group/*"default" */, int order/*-1*/) 
{
//  cout << "adding double: " << flag << " with: " << shortDescription << endl ;
	mVariableList.push_back( new DoubleType(variable,flag,shortDescription,defaultValue,longDescription,group,order)) ;  
}

void AutoOptionList::addOption(string* variable,string flag,string shortDescription,string defaultValue/*'a'*/, string longDescription/*""*/,string group/*"default" */, int order/*-1*/) 
{
	mVariableList.push_back( new OptionType(variable,flag,shortDescription,defaultValue,longDescription,group,order)) ;  
}

// for every input map to variables
void AutoOptionList::parseOption()  
{
	 list<GeneralType*>::iterator iter = mVariableList.begin() ; 
	 while(iter!=mVariableList.end()){
		 if( (*iter)->isInt()){
   		 ((IntType*) *iter)->setInt( getInt((*iter)->getFlag(),((IntType*) *iter)->getDefaultInt())) ; 
		 }
		 else
		 if( (*iter)->isDouble()){
   		 ((DoubleType*) *iter)->setDouble( getDouble((*iter)->getFlag(),((DoubleType*) *iter)->getDefaultDouble())) ; 
		 }
		 else
		 if( (*iter)->isOption()){
   		 ((OptionType*) *iter)->setOption( getOption((*iter)->getFlag(),((OptionType*) *iter)->getDefaultOption())) ; 
		 }
		 else
		 if( (*iter)->isChar()){
   		 ((CharType*) *iter)->setChar( getChar((*iter)->getFlag(),((CharType*) *iter)->getDefaultChar())) ; 
		 }
		 else
		 if( (*iter)->isFlag()){
   		 ((FlagType*) *iter)->setFlag( getFlag((*iter)->getFlag(),((FlagType*) *iter)->getDefaultFlag())) ; 
		 }
		 ++iter ; 
	 }
}


void AutoOptionList::standardOptions() 
{
	 if( getFlag("options",false) || getFlag("opts",false)){
		 cout << getOptionValueText() << endl; 
	 }
	 else
	 if( getFlag("xml",false) ){
		 string xmlString = getOptionXML() ; 
		 cout << xmlString << endl ;; 
		 ofstream outStream("tmpfile.txt") ; 
		 outStream << xmlString << endl ; 
		 outStream.close() ; 
		 exit(0) ; 
	 }
	 else
	 if( mNumArgs ==1 || getFlag("help",false) || getFlag("h",false)){
		 cout << getHelpText() << endl; 
		 exit(0) ; 
	 }
	 else{
		 cout << getHelpText() << endl; 
	 }

	 // dry-run
	 if( getFlag("n",false) ){
		  exit(0) ; 
	 }
}




